Blog

Jon Clausen

October 03, 2022

Spread the word


Share your thoughts

There are times when code needs to be shipped in a compiled state. It might be for obfuscation or source protection, it might just because it runs faster that way, without the CFML server needing to compile templates at runtime. It's an excellent use case for production Docker images and code deploy pipelines.

If you are running your applications on Lucee, the bytcode compilation is a bit different than the one Adobe performs. In that case, your best option is to use the cfml-compiler module on Forgebox. The downside to Lucee's compilation is that it doesn't package an entire application to the target directory. Instead, Lucee will only compile and move .cfm and .cfc files it encounters, leaving all other files untouched. Because of this, it is best to make a copy of your source code and compile in-place so all application assets ( JS, CSS, images ) are co-located in the final package.

In build environments, it's often unecessary to go through the steps to install Commandbox other dependencies because Docker is usually readily available. We can use the presence of Docker to run one-off commands to perform steps like installing CommandBox dependencies:

docker run --rm -v $PWD:/app ortussolutions/commandbox /bin/bash -c "box install"

The above one-liner will perform a box install on the source code in your current working directory. Note the use of the --rm flag, which will remove the container after the script has finished. To compile Lucee source code in-place, overwriting the existing uncompiled templates, the command would like something like this:

docker run --rm -v $PWD:/app ortussolutions/commandbox /bin/bash -c "box install cfml-compiler && box cfcompile box cfcompile sourcePath=./ destPath=./ overwrite=true cfengine=lucee@5.3.9"

For Adobe Coldfusion source code, because it will also copy over non-CFML files, you can compile in to a separate directory without losing those assets. The process is a bit more complex, in which case, rather than passing in a single command, we will create a shell script which we can mount in and pass in to the command of the container. To do this, we'll create a file called compile-source.sh and place it in to a util folder above the root where we will be running our Docker commands:

#!/bin/bash
export CFUSION_HOME=/usr/local/lib/serverHome/WEB-INF/cfusion
export WEBINF=/usr/local/lib/serverHome/WEB-INF
export COMMANDBOX_HOME=/usr/local/lib/CommandBox
export CLASSES=$webroot/lib/java/*:$WEBINF/lib/*:$CFUSION_HOME/lib/*:$CFUSION_HOME/lib/updates/*:$WEBINF/lib/etc/*:$CFUSION_HOME/lib/axis2/*:$WEBINF/lib/oosdk/lib/*:$WEBINF/flex/jars/*:$WEBINF/cfform/jars/*:$CFUSION_HOME/jetty/lib/*:/usr/local/lib/serverHome/CFIDE/administrator/classes/*:/usr/local/lib/serverHome/CFIDE/classes/*:$CFUSION_HOME/jintegra/lib/*:$CFUSION_HOME/jintegra/*:$COMMANDBOX_HOME/lib/*

PATH=$PATH:$CFUSION_HOME/bin
export PATH

# Install some java libraries in to the classpath which are not present in the J2EE deployment:
apt update && apt install -y wget && \
cd /tmp && \
    wget --content-disposition https://search.maven.org/remotecontent?filepath=javax/servlet/javax.servlet-api/4.0.1/javax.servlet-api-4.0.1.jar && \
    wget --content-disposition https://search.maven.org/remotecontent?filepath=jetty/jsp-api/2.1-6.0.2/jsp-api-2.1-6.0.2.jar && \
    mv /tmp/*.jar /usr/local/lib/serverHome/WEB-INF/lib/ && cd /app

echo "Compiling source at $srcdir to $webroot"

# Compile our source in to the specified directory
# The use of --add-opens assures that all members of those named classes are accessible for reflection, if necessary
/opt/java/openjdk/bin/java --add-opens=java.base/java.nio=ALL-UNNAMED \
            --add-opens=java.base/java.lang=ALL-UNNAMED \
            --add-opens=java.base/sun.util.cldr=ALL-UNNAMED \
            --add-opens=java.base/sun.util.locale.provider=ALL-UNNAMED \
            -cp $CLASSES -Dcoldfusion.classPath=$CFUSION_HOME/lib/updates,$CFUSION_HOME/lib \
            -Dcoldfusion.libPath=$CFUSION_HOME/lib \
            coldfusion.tools.CommandLineInvoker Compiler \
            -deploy -webinf $WEBINF -webroot $webroot -cfroot $CFUSION_HOME -srcdir $webroot -deploydir $deploydir

Note the two java JARs which we download and place in to the classpath, before running the compilation. These JARS are not present in the J2EE version of Adobe Coldfusion, which is what is used in the Docker containers.

Once our shell script has been saved, our command to compile ACF, using the ACF 2018 container code looks like this:

docker run --rm -v $PWD:/app -v $PWD/tmp:/tmp/src -v $PWD/../util/compile-source.sh:/usr/local/lib/compile-source.sh \
-e "webroot=/app" -e "srcdir=/app" -e "deploydir=/tmp/src" \
ortussolutions/commandbox:adobe2018 \
/usr/local/lib/compile-source.sh

With this command, we are mounting in our current working directory to the convention /app directory in the container. We are also mounting a folder named tmp which will be created by the container if it does not exist. Lastly, we mount in our compile-source.sh script and use it as the entrypoint for the container which then runs that script only, when the container comes online.

Due to the fact that many Open source libraries ( such as Coldbox ) support both Lucee and Adobe, you will want to run your code compilation before you run your box install. This is because there may be Java classes referenced, which are Lucee-only, which will throw errors when attempting to compile for Adobe Coldfusion, since those classes are not present.

This same command works on the Adobe 2021 container, as well, however you will need to install any cfpm modules referenced in your code in the compile-source.sh script.

By shipping your applications to production with compiled source code, you will notice a significant performance improvement - especially on application startup - since no compilation of your source code is necessary.

Add Your Comment

Recent Entries

The Hidden Costs of In-House Database Management

The Hidden Costs of In-House Database Management

The Hidden Costs of In-House Database Management


Opting for in-house database management involves more than just a salary. Here are some often-overlooked costs associated with maintaining your own DBA team.



1. High Salaries and Benefits


Hiring skilled DBAs is expensive. According to industry reports, the average salary of a DBA in the U.S. can range from $85,000 to over $130,000 per year, depending on experience and expertise. When you add ...

Cristobal Escobar
Cristobal Escobar
November 20, 2024
5 Signs It’s Time to Modernize Your ColdFusion / CFML Application

5 Signs It’s Time to Modernize Your ColdFusion / CFML Application

ColdFusion has long been a reliable platform for building web applications, but like any technology, it requires maintenance and modernization over time. Whether you're using Lucee or Adobe ColdFusion, it’s critical to recognize the signs that your application is no longer meeting today’s standards in performance, security, and scalability. Let’s explore five clear indicators that it’s time to modernize your ColdFusion application and how ColdFusion consulting can help breathe new life into y...

Cristobal Escobar
Cristobal Escobar
November 19, 2024
ColdBox Free Tip 5 - Building Named Routes with a Struct

ColdBox Free Tip 5 - Building Named Routes with a Struct

**Did you know ColdBox provides flexible ways to build routes using structs?** In this tip, we’ll cover how to use the `event.buildLink()` and `event.route()` methods for named routes, a feature that’s especially handy when working with dynamic URLs.

Maria Jose Herrera
Maria Jose Herrera
November 19, 2024